package metainv;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import java.util.Set;
import java.util.StringTokenizer;

import daikon.Daikon;
import daikon.asm.X86Instruction;


public class MetaConstraintsIO {
	public static String var_regexp = "";
	public static Set<MetaInvInfoConditional> conditionalSet = new HashSet<MetaInvInfoConditional>();
	public static Set<MetaInvInfo> concordSet = new HashSet<MetaInvInfo>();
	public static Set<MetaInvInfo> drfcSet = new HashSet<MetaInvInfo>();
	public static Set<String> mPpts = new HashSet<String>();
	
	public void readInputFile(String aRFile) throws IOException, ClassNotFoundException, SecurityException, NoSuchMethodException {
		int lbracket, rbracket;
		File file = new File(aRFile);
		FileReader fr = new FileReader(file);
		boolean onlyCareAbout = false;
		LineNumberReader reader = new LineNumberReader(fr);
		
		try {
			for (String line = reader.readLine(); line != null;
			line = reader.readLine()) {
				if (line.equals("")) {
					continue;
				}
				
				lbracket = line.indexOf("(");
	            rbracket = line.lastIndexOf(")");
	            
	            if(line.startsWith("OnlyCareAboutField")) {
					String[] m = getParamTypes(line);
					Daikon.class_field.put(m[0], "this." + m[1]);
				}
				
	            
				else if(line.startsWith("OnlyCareAboutVariable")) {
					if(!onlyCareAbout) {
						var_regexp = line.substring(lbracket+1, rbracket);
						//onlyCareAbout can be used only once
						onlyCareAbout = true;
					}
					else
						break;
				} 
				
				else if(line.startsWith("Conditional")) {
					int slbracket = line.indexOf("(", lbracket+1);
					String fullMethName = line.substring(lbracket+1, slbracket);
					int lastdot = fullMethName.lastIndexOf(".");
	            	String className = fullMethName.substring(0, lastdot);
	            	Class target = Class.forName(className);
	            	String simpleMethName = fullMethName.substring(lastdot+1, fullMethName.length());
	            	int srbracket = line.indexOf(")");
	            	StringTokenizer pparamTypes = new StringTokenizer(line.substring(slbracket+1, srbracket), ",");
	            	String param;
	            	List<Class> paramlist = new ArrayList<Class>();
	            	while(pparamTypes.hasMoreTokens()){
	            		  param = pparamTypes.nextToken().trim();
	            		  if(param.equals("int"))
	            			  paramlist.add(Integer.TYPE);
	            		  else if(param.equals("byte"))
	            			  paramlist.add(Byte.TYPE);
	            		  else if(param.equals("short"))
	            			  paramlist.add(Short.TYPE);
	            		  else if(param.equals("long"))
	            			  paramlist.add(Long.TYPE);
	            		  else if(param.equals("float"))
	            			  paramlist.add(Float.TYPE);
	            		  else if(param.equals("double"))
	            			  paramlist.add(Double.TYPE);
	            		  else if(param.equals("char"))
	            			  paramlist.add(Character.TYPE);
	            		  else if(param.equals("boolean"))
	            			  paramlist.add(Boolean.TYPE);
	            		  else
	            			  paramlist.add(Class.forName(param));
	                }

	            	Class[] parameterTypes = (Class[])paramlist.toArray(new Class[paramlist.size()]);
	            	Method meth = target.getDeclaredMethod(simpleMethName, parameterTypes);
	            	int scomma = line.indexOf(",", srbracket);
	            	String cconstraint = line.substring(scomma+1, rbracket);
	            	handleConditional(cconstraint, meth);
				}
				
				else if(line.startsWith("Subdomain") || line.startsWith("Subrange") || line.startsWith("Follows") || line.startsWith("CanFollow")) {
					handleOther(line);
				}
	            
				else if(line.startsWith("Concord")) {
					handleConcord(line);
				}
				
				else {
	            	throw new Error("Illege Input!");
	            }
			}
		} 
		finally {
			if (fr != null)
				fr.close();
			if (reader != null)
				reader.close();
		}
	}
	
	public void handleConditional(String constraint, Method meth) {
		String[] m = getParamTypes(constraint);
		conditionalSet.add(new MetaInvInfoConditional(getConstraintsType(constraint), m[0], m[1], meth));
	}
	
	public void handleConcord(String constraint) {
		String[] m = getParamTypes(constraint);
		ConstraintType type = getConstraintsType(constraint);
//		if (type.equals(ConstraintType.Concord)) {
//			mPpts.add(m[0]+":::ENTER");
//			mPpts.add(m[1]+":::ENTER");
//			mPpts.add(m[0]+":::EXIT");
//			mPpts.add(m[1]+":::EXIT");
//		}
		concordSet.add(new MetaInvInfo(type, m[0], m[1]));
	}
	
	public void handleOther(String constraint) {
		String[] m = getParamTypes(constraint);
		ConstraintType type = getConstraintsType(constraint);
//		if (type.equals(ConstraintType.Subdomain)) {
//			mPpts.add(m[0]+":::ENTER");
//			mPpts.add(m[1]+":::ENTER");
//		}
//		else if (type.equals(ConstraintType.Subrange)) {
//			mPpts.add(m[0]+":::EXIT");
//			mPpts.add(m[1]+":::EXIT");
//		}
//		else if (type.equals(ConstraintType.CanFollow)) {
//			mPpts.add(m[0]+":::EXIT");
//			mPpts.add(m[1]+":::ENTER");
//		}
//		else if (type.equals(ConstraintType.Follows)) {
//			mPpts.add(m[0]+":::ENTER");
//			mPpts.add(m[1]+":::EXIT");
//		}
		drfcSet.add(new MetaInvInfo(type, m[0], m[1]));
	}
	
	public String[] getParamTypes(String paramconstraint) {
		int lbracket = paramconstraint.indexOf("(");
		int rbracket = paramconstraint.lastIndexOf(")");
		String ppt2s = paramconstraint.substring(lbracket+1, rbracket);
		int r2bracket =  ppt2s.indexOf(")");
		int comma = ppt2s.indexOf(",", r2bracket);
		
		String[] m = new String[2];
		m[0] = ppt2s.substring(0, r2bracket+1).trim();
		m[1] = ppt2s.substring(comma+1, ppt2s.length()).trim();
		
		return m;
	}
	
	public ConstraintType getConstraintsType(String constraint) {
		constraint = constraint.trim();
		
	    if(constraint.startsWith("Subrange")) {
			return ConstraintType.Subrange;
		}
			
		else if(constraint.startsWith("Subdomain")) {
			return ConstraintType.Subdomain;
		}
		
		else if(constraint.startsWith("Follows")) {
			return ConstraintType.Follows;
		}
		
		else if(constraint.startsWith("Concord")) {
			return ConstraintType.Concord;
		}
		
		else if (constraint.startsWith("CanFollow")) {
			return ConstraintType.CanFollow;
		}
	    
		else
		    return null;
	    
	}
}
